#include "mp5.h"

struct job {
  int time_left;
  int arrival_time;
  short int active;
  int priority;
};

struct job jobs[10];

char timing_diagram[256];

int main(int argc, char **argv) {
  FILE *fp;
  int i, rr;
  int quantum, next_quantum; 
  int next_job, current_job, total_jobs;
  int current_time;
  char scheme_name[5], s1[16];


  if(argc<3) {
    fprintf(stderr,"Usage: %s <scheme> <jobs file>\n",argv[0]);
    exit(-1);
  }
  strncpy(scheme_name, argv[1],4);
  scheme_name[4]='\0';
  if(!strncmp(scheme_name,"rr",2)) {
    rr=1;
    scheme = RR;
    quantum = atoi(scheme_name+2);
    if (quantum <=0) {
      fprintf(stderr, "Invalid time quantum.\n");
      exit(-4);
    }
  } else{
    rr=0;
    quantum = 0;
    if (!strcmp(scheme_name,"fcfs"))
      scheme = FCFS;
    else if (!strcmp(scheme_name,"sjf"))
      scheme = SJF;
    else if (!strcmp(scheme_name,"psjf"))
      scheme = PSJF;
    else if (!strcmp(scheme_name,"pri"))
      scheme = PRI;
    else if (!strcmp(scheme_name,"ppri"))
      scheme = PPRI;
    else {
      fprintf(stderr,"Invalid scheduling algorithm.\n");
      fprintf(stderr,"Acceptable algorithms are:\n");
      fprintf(stderr,"\tfcfs sjf psjf pri ppri rr#\n");
      exit(-6);
    }
  }

  // Collect process data in advance, store in jobs[]
  fp = fopen(argv[2],"r");
  if (fp == NULL) {
    perror("Can't open input file");
    exit(-2);
  }
  i=0;

  if (fscanf(fp,"\"%15[^,\"]\",\"%15[^,\"]\",\"%15[^,\"]\"\n",s1,s1,s1) != 3) {
    fprintf(stderr,"Invalid input file format.\n");
    exit(-5);
  }
  while(fscanf(fp,"%d,%d,%d\n",&(jobs[i].arrival_time),&(jobs[i].time_left),&(jobs[i].priority))>0) {
    jobs[i].active = 0;
    i++;
  }
  printf("Loaded %d jobs.\n\n\n",i);

  // initialize
  total_jobs = i;
  next_job = 0;
  current_job = -1;
  current_time = 0;

  // loop as long as there is a current job, or more jobs to process later
  while(current_job >=0 || next_job < total_jobs) { 

    /* For every cycle (when appropriate):
     * 1) Update quantum
     * 2) Update timing diagram
     * 3) Update job status
     * 4) Call scheduler
     * 5) Update clock
     *
     */


    int next_start, next_finish;

    // Show status
    printf("Current time: %d\t Current job %d\n",current_time, current_job);
    printf("Timing diagram: %s\n",timing_diagram);
    printf("Process queue: ");
    show_queue();
    printf("\n\n");

    if (current_job == -1) {       // Queue empty
      if (next_job >= total_jobs)
	break;                    // no more jobs: quit
      else {                      // wait for new job
	next_start = jobs[next_job].arrival_time;
	jobs[next_job].active = 1;
	if (rr)
	  next_quantum = ((int)(current_time/quantum+1))*quantum;
	for(i=current_time;i<next_start;i++)
	  timing_diagram[i]='-';
	printf("New job %d at time %d.\n", next_job, next_start);
	current_job = new_job(next_job, next_start, 
			      jobs[next_job].time_left, 
			      jobs[next_job].priority);
	current_time = next_start;
	next_job++;
      }
      continue;
    }

    // Only valid jobs should be processed
    if(!jobs[current_job].active) {
      fprintf(stderr, "Scheduler selected invalid job.\n");
      exit(-3);
    }

    /* There are 3 possible events: a new job, a job finishes, and 
     * the quantum expires.  Check all three and take the earliest
     * one, as long as it is valid.  In case of a tie, a finished 
     * job takes prioirty over a new quantum, which takes priority 
     * over a new job.
     */

    if (rr && next_quantum < current_time)
      next_quantum += quantum;

    next_start = jobs[next_job].arrival_time;
    next_finish = current_time + jobs[current_job].time_left;

    if(rr && (next_quantum <= next_start || next_job >= total_jobs) && next_quantum < next_finish) {
      for(i=current_time;i<next_quantum;i++)
				timing_diagram[i] = current_job + '0';
      jobs[current_job].time_left -= (next_quantum - current_time);
      printf("Tick at time %d.\n",next_quantum);
      current_job = quantum_expired(next_quantum);
      current_time = next_quantum;
      next_quantum += quantum;
    } else if (next_start < next_finish && next_job < total_jobs) {
      for(i=current_time;i<next_start;i++)
				timing_diagram[i] = current_job + '0';
      jobs[current_job].time_left -= (next_start - current_time);
      jobs[next_job].active = 1;
      printf("New job %d at time %d.\n", next_job, next_start);
      current_job = new_job(next_job, next_start, jobs[next_job].time_left, jobs[next_job].priority);
      current_time = next_start;
      next_job++;
    } else {
      for(i=current_time;i<next_finish;i++)
				timing_diagram[i] = current_job + '0';
      jobs[current_job].time_left -= (next_finish - current_time);
      jobs[current_job].active = 0;
      printf("Job %d finished at time %d.\n", current_job, next_finish);
      current_job = job_finished(current_job, next_finish);
      current_time = next_finish;
    }
  }

  timing_diagram[current_time]='\0';

  printf("\n\nFinal timing diagram:\n\t%s\n",timing_diagram);
  printf("Average Response Time: %1.2f\n", average_response_time());
  printf("Average Wait Time: %1.2f\n", average_wait_time());
  printf("Average Turnaround Time: %1.2f\n", average_turnaround_time());
}
